/*  Copyright (c) 2012 William Rogers and James Boud

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

using System;
using SlimDX;
using SlimDX.Direct3D11;
using JBBRXG11;

namespace JBBRXG11
{   
    // Summary:
    //     A vertex declaration, which defines per-vertex data.
    
    public class VertexDeclaration : IDisposable
    {
        const int MAXSLOTS = 5;
        GraphicsDevice device;
        Microsoft.Xna.Framework.Graphics.VertexElement[] xna_elements;
        SlimDX.Direct3D11.InputElement[] vertexelements;
        int stridesize;
        bool isdisposed;
        static int nameindex = 0;
        string name;
        object tag;
        bool awaiting_first_use; // Because a layout cannot be assigned in DX10 without
                                 // checking it against an effect signature - we just set
                                 // this flag when a vertex declaration is first created
                                 // and defer creating the layout untile the vertex
                                 // declaration is first used by an effect

                                 // Note also that there is nothing (SlimDX cached) to 
                                 // dispose until the layout is created, so registering
                                 // this as needing disposal is deferred until then, at
                                 // which time a device is available for the registration
        InputLayout layout;
        
        // Summary:
        //     Creates an instance of this object.
        //
        // Parameters:
        //   elements:
        //     [ParamArrayAttribute] An array of per-vertex elements.
        public VertexDeclaration(params Microsoft.Xna.Framework.Graphics.VertexElement[] elements)
        {
            stridesize = 0;
            // These item defaults are just to prevent the compiler complaining
            string semantic = "";
            SlimDX.DXGI.Format format = SlimDX.DXGI.Format.R32G32B32A32_Float;
            int elsize = 12;

            xna_elements = elements;
            vertexelements = new InputElement[elements.Length];
            for (int e = 0; e < elements.Length; e++)
            {
                semantic = "UNKNOWN";
                format = SlimDX.DXGI.Format.Unknown;
                switch (elements[e].VertexElementUsage)
                {
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Binormal:
                        semantic = "BINORMAL"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendIndices:
                        semantic = "BLENDINDICES"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.BlendWeight:
                        semantic = "BLENDWEIGHTS"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Color:
                        semantic = "COLOR"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Depth:
                        semantic = "DEPTH"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Fog:
                        semantic = "FOG"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Normal:
                        semantic = "NORMAL"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.PointSize:
                        semantic = "POINTSIZE"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Position:
                        semantic = "POSITION"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Sample:
                        semantic = "SAMPLE"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.Tangent:
                        semantic = "TANGENT"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.TessellateFactor:
                        semantic = "TESSELLATEFACTOR"; break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementUsage.TextureCoordinate:
                        semantic = "TEXTURECOORD"; break;
                    default: throw new Exception("Unknown semantic type");
                }

                switch (elements[e].VertexElementFormat)
                {
                    case Microsoft.Xna.Framework.Graphics.VertexElementFormat.Color:
                        format = SlimDX.DXGI.Format.R8G8B8A8_UNorm;
                        elsize = 4;
                        break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementFormat.Single:
                        format = SlimDX.DXGI.Format.R32_Float;
                        elsize = 4;
                        break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementFormat.Vector2:
                        format = SlimDX.DXGI.Format.R32G32_Float;
                        elsize = 8;
                        break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementFormat.Vector3:
                        format = SlimDX.DXGI.Format.R32G32B32_Float;
                        elsize = 12;
                        break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementFormat.Vector4:
                        format = SlimDX.DXGI.Format.R32G32B32A32_Float;
                        elsize = 16;
                        break;
                    case Microsoft.Xna.Framework.Graphics.VertexElementFormat.Byte4:
                        format = SlimDX.DXGI.Format.R8G8B8A8_UInt;
                        elsize = 4;
                        break;
                    // and all the rest  TODO: More VertexElementFormats
                    default: throw new Exception("Vertex element format not yet implemented");
                }

                vertexelements[e] = new InputElement(semantic, elements[e].UsageIndex, format, elements[e].Offset, 0);
                stridesize += elsize;
            }
            isdisposed = false;
            name = "VertexDeclaration" + (nameindex++).ToString();
            tag = null;

            awaiting_first_use = true;
            layout = null;
        }
        //
        // Summary:
        //     Creates an instance of this object.
        //
        // Parameters:
        //   vertexStride:
        //     The number of bytes per element.
        //
        //   elements:
        //     [ParamArrayAttribute] An array of per-vertex elements.
        public VertexDeclaration(int vertexStride, params Microsoft.Xna.Framework.Graphics.VertexElement[] elements) : this(elements)
        {
            stridesize = vertexStride;
        }
        internal bool AwaitingUse { get { return awaiting_first_use; } }

        internal void CreateLayout(GraphicsDevice device, SlimDX.D3DCompiler.ShaderSignature shader_signature)
        {
            try
            {
                layout = new InputLayout(device.DX_Device,
                                         shader_signature,
                                         vertexelements);
                GraphicsDeviceManager.GetGame(device).UserContentAdd(this);  // Register for disposal
            }
            catch (Exception)
            {
                throw new Exception("JBBRXG11: Mismatch between shader and vertex structure.\r\n" +
                                    "If you are using BasicEffect, this can result from\r\n" +
                                    "enabling lighting without normals, vertex colours without\r\n" +
                                    "vertex colour data, or textures without texture coordinates.");
            }
            awaiting_first_use = false;
        }

        internal InputLayout VertexLayout { get { return layout; } }

        public GraphicsDevice GraphicsDevice { get { return device; } }

        public int GetVertexStrideSize()
        {
            return stridesize;
        }

        public void Dispose()
        {
            if (!isdisposed)
            {
                if (!awaiting_first_use) layout.Dispose();
                isdisposed = true;
            }
        }

        public bool IsDisposed { get { return isdisposed; } }
        public string Name { get { return name; } set { name = value; } }
        public object Tag { get { return tag; } set { tag = value; } }
        public Microsoft.Xna.Framework.Graphics.VertexElement[] GetVertexElements() { return xna_elements; }
    }
}

/*
        public event EventHandler Disposing;

        //
        // Summary:
        //     Immediately releases the unmanaged resources used by this object.
        //
        // Parameters:
        //   :
        //     [MarshalAsAttribute(U1)] true to release both managed and unmanaged resources;
        //     false to release only unmanaged resources.
        protected virtual void Dispose(bool __p1);

        //
        // Summary:
        //     Gets the size of a vertex from the vertex declaration.
        //
        // Parameters:
        //   elements:
        //     The vertex declaration.
        //
        //   stream:
        //     The zero-based stream index.
        public static int GetVertexStrideSize(VertexElement[] elements, int stream);
        //
        // Summary:
        //     Raises the Disposing event when called from within a derived class.
        //
        // Parameters:
        //   value0:
        //     Invoking object reference; should be this object.
        //
        //   value1:
        //     Arguments to pass to the event handler.
        protected void raise_Disposing(object value0, EventArgs value1);
        //
        // Summary:
        //     Retrieves a string representation of this object.
        public override string ToString();
 */
